package entities {
	import blocks.FallingTrap;
	
	import com.greensock.TweenMax;
	import com.greensock.easing.Elastic;
	
	import org.flixel.*;
	import org.flixel.plugin.photonstorm.FlxDelay;
	import org.flixel.plugin.photonstorm.FlxMath;

	public class Abilities {
		private var parent:Entity;
		private var attackName:String;
		private var attackDuration:FlxDelay;
		private var attackInfos:Object;
		public var allowed:Array;
		
		
		public function Abilities(Parent:Entity){
			parent = Parent;
			
			if(Registry.abilities[parent.name]) setAllowedAbilities();
		}
		
		
		public function attack(attackName:String = null):void {
			
			//check if this unit have abilities
			if(!Registry.abilities || !Registry.abilities[parent.name]) return;
			
			if(can(attackName) ) {
				attackInfos = Registry.attacks[attackName];
				
				switch(attackName){
					case 'spawnMinions' : 
						spawnMinions();
						break;
					case 'charge' :
						charge();
						break;
					case 'superAttack' : 
						superAttack();
						break;
					case 'objectRain' :
						objectRain();
						break;
					case 'switch' :
						switchPlayer();
						break;
					case 'invincibility' :
						invincibility();
						break;
					default :
						return;
				}
			}
		}
		
		
		public function randomAttack():void {
			// randomize attack
			if(allowed.length > 0) {
				var random:int = FlxMath.rand(0, allowed.length);
				var attackName:String = allowed[random];
				switch(attackName) {
					case 'switchPlayer':
					case 'invincibility':
						randomAttack();
						break;
					default : 
						attack(attackName);
				}
			}
		}
		
		
		/**
		 * This function will fill the list of the allowed abilities, and 'll be updated for each parent leveling up
		 * */
		public function setAllowedAbilities():void {
			var abilities:Object = Registry.abilities[parent.name];
			allowed = [];
				
			//recreate allowed list
			for (var i:int = 0 ; i <= parent.level ;  i++){
				if(abilities && abilities[i]){
					for each( var attackName:String in abilities[i].attackName )
						allowed.push(attackName);
				}
			}
		}
		
		public function can(attackName:String):Boolean {
			if(!Registry.attacks[attackName]){
				//trace(attackName, 'doesn\'t exists');
				return false;
			}
			if(allowed.indexOf(attackName) == -1){
				//trace('you can\'t do', attackName);
				return false;
			} 
			//trace('lets',attackName);
			return true;
		}
		
		
		
		
		
		/********************************************************
		 * 														*
		 * 														*
		 * 					ATTACKS LIST						*
		 * 														*
		 * 														*
		 ********************************************************/
		
		
		//  **  Charging  **
		private function charge():void {
			parent._charging = true;
			Enemy(parent)._tracking = false;
			
			// add effect
			//Registry.fx.addBlur(parent);			
			
			// facing player
			parent.facing = (getPlayerPosition() > 0) ? FlxObject.RIGHT : FlxObject.LEFT;			
			
			// move to player
			parent.velocity.x = getPlayerPosition() * parent.infos.gamePlay.speed * attackInfos.speedFactor;
			
			// play sound if defined
			if(parent.infos.sounds.charge) parent.infos.sounds.charge.play();						
			
			attackDuration = new FlxDelay(attackInfos.duration*1000);
			attackDuration.callback = function():void{
				parent._charging = false;
				Enemy(parent)._tracking = parent.infos.gamePlay.tracking;
				parent.drag.x = 150;
				parent.velocity.x = (parent.facing == FlxObject.RIGHT) ? parent.infos.gamePlay.speed : -parent.infos.gamePlay.speed;
				Registry.fx.removeBlur();
			}
			attackDuration.start();
		}
		
		
		
	
		//  ** Super Attack  **
		private function superAttack():void {
			var entitiesOnScreen:Array = getEntitiesOnScreen();
			
			if(entitiesOnScreen.length > 0 ) {
				if(parent.infos.sounds.superAttack) parent.infos.sounds.superAttack.play();
				
				parent._invincible = Registry.player._attacking = true;
				
				// take another entity infos if exists
				var infos:Object = infos = parent.infos;
				for(var key:String in Registry.entityInfos){
					if(Registry.entityInfos[key].type == 'player' && key != parent.name) {
						infos = Registry.entityInfos[key];
						break;
					}
				}

				var player1:Entity =  new Entity(Registry.player.x, Registry.player.y, infos);
				
				var timer:FlxTimer = new FlxTimer();
				
				timer.start(.6,entitiesOnScreen.length,function():void {
					var entity:Entity = entitiesOnScreen[timer.loopsLeft] as Entity; 
					
					if(timer.loopsLeft >= 0 && entity  )
						TweenMax.to(player1, .5, { x: entity.x, y: entity.y,  ease:Elastic.easeOut, onComplete: saComplete }); 
					
					function saComplete():void{
						if(timer.loopsLeft <= 0){
							Registry.player._attacking = parent._invincible = false;
							player1.kill();
							return;
						}
						
						player1.play('attack',true);
						FlxG.camera.flash(0xFFFFFFFF,.5);
						entity.hurt(attackInfos.damages); 
					}
				});
			}	
		}
		
		
		
		/****************************
		 * 		Object Rain 		*
		 ****************************/
		private function objectRain():void{
			
			//attackInfos = Registry.attacks['objectRain'];
			
			var fallings:FlxGroup = new FlxGroup(attackInfos.quantity);
			parent._invincible = parent._attacking = true;
			
			for(var i:int; i<attackInfos.quantity; i++){
				var infos:Object =  {
					'duration' : attackInfos.duration,
					'speed' : FlxMath.randFloat(0,.2),
					'damages' : attackInfos.damages,
					'tileInfos' : {
						'tileSize' : new Array(attackInfos.image.width, attackInfos.image.height),
						'tileSheet' : attackInfos.image
					}
				}
				var object:FallingTrap = new FallingTrap(parent.x + FlxMath.rand(-300,300) , parent.y - infos.tileInfos.tileSize.height, infos);
				object.activate();
				fallings.add(object);
			}
			
			attackDuration = new FlxDelay(infos.duration*1000);
			attackDuration.callback = function():void { 
				for each( object in fallings.members) { fallings.remove(object, true); }
				parent._invincible = parent._attacking = false;
				fallings.kill(); 
			}
			attackDuration.start();
		}
		
		
		
		
		// ** SpawnMinions **
		private function spawnMinions():void {
			for (var i:uint = 0; i < attackInfos.quantity; i++) {
				var minion:Minion = Registry.enemyPool.getFirstAvailable(Minion) as Minion;
				
				if (minion == null) {
					if(Registry.entityInfos[attackInfos.minionName]) 
						minion = new Minion(parent.x, parent.y, Registry.entityInfos[attackInfos.minionName]);
					else 
						trace('aucune entité ne porte ce nom : ' + attackInfos.minionName);
				} else {
					minion.reset(parent.x, parent.y);
				}
				minion.velocity.x =  getPlayerPosition() * (10 + 300 * Math.random());
				minion.velocity.y = -300 - 400 * Math.random();
			}
			
			parent._attacking = true;
			attackDuration = new FlxDelay(attackInfos.duration*1000);
			attackDuration.callback = function():void{ parent._attacking = false; }
			attackDuration.start();
				 
		}
		
		
		
		
		
		
		/********************************************
		 * 											*
		 * 											*
		 * 		NON AGGRESSIVES ABILITIES			*
		 * 											*
		 * 											*
		 ********************************************/
		
		
		// ** switch player **
		public static function switchPlayer():void{
			var oldPlayer:Player = Registry.player;
			
			oldPlayer.visible = false;
			oldPlayer.active = false;
			oldPlayer.alive = false;
			oldPlayer.exists = false;
			
			var nextPlayerName:String = (Registry.playerNames[Registry.playerNames.indexOf(oldPlayer.infos.name) + 1]) ? Registry.playerNames[Registry.playerNames.indexOf(oldPlayer.infos.name) + 1] : Registry.playerNames[0]
			Registry.player = new Player(oldPlayer.x, oldPlayer.y - (Registry.entityInfos[nextPlayerName].tile.realSize[1] - oldPlayer.height), Registry.entityInfos[nextPlayerName]);
			Registry.player.health = oldPlayer.health;
			Registry.player.level = oldPlayer.level;
			Registry.player.abilities.setAllowedAbilities();
			Registry.player.infos.gamePlay.lives = oldPlayer.infos.gamePlay.lives;
			Registry.player.score = oldPlayer.score;
			
			Registry.hud.healthbar.setParent(Registry.player,'health');
			FlxG.camera.follow(Registry.player, FlxCamera.STYLE_PLATFORMER); //camera follows
		}
		
		
		/**	
		 *  Invulnerability
		 * 	@param	duration	if set on -1 it will just switch the invicibitlity statement, if not set, it will use capacity params
		 * */
		public function invincibility():void{
			
			parent._invincible = true;
			parent.alpha =  .7;
			
			attackDuration = new FlxDelay(attackInfos.duration*1000);
			attackDuration.callback = function():void { parent.alpha = 1;parent._invincible = false;	}
			attackDuration.start();
		}
		
		
		
		
		
		/********************************************************
		 * 														*
		 * 					USEFUL FUNCTIONS					*
		 * 														*
		 ********************************************************/
		
	
		private function getPlayerDistance():Number {
			return FlxU.getDistance(Registry.player.getMidpoint(), parent.getMidpoint());
		}
		
		
		private function getPlayerPosition():int {
			if (Registry.player.x > parent.x)
				return 1;
			return -1;
		}
		
		
		
		private static function getEntitiesOnScreen():Array{
			var entitiesArray:Array = [];
			for(var i:uint ; i<Registry.enemyPool.members.length ; i++){
				if(Registry.enemyPool.members[i] && Registry.enemyPool.members[i].onScreen()){
					entitiesArray.push(Registry.enemyPool.members[i])
				}
			}
			return entitiesArray;
		}

	
		
		/*
		private function erasePartOfLand(bullet:FlxObject, theLand:FlxObject):void
		{
		if (FlxCollision.pixelPerfectCheck(bullet as FlxSprite, land))
		{
		//	Work out where into the image the bullet has landed
		var offsetX:int = bullet.x - land.x;
		var offsetY:int = bullet.y - land.y;
		
		//	Grab the BitmapData from the image, so we can modify it
		var temp:BitmapData = land.pixels;
		
		//	This erases a rect area of the image - but you could also draw a circle into it, or anything really
		temp.fillRect(new Rectangle(offsetX, offsetY, damageSize, damageSize), 0x0);
		
		//	Write it back again
		land.pixels = temp;
		
		//	And remove the bullet - you don't have to do this, it can make some interest effects if you don't!
		bullet.kill();
		}
		}*/
	}
}